/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename:  function/update_hash.cc
 */

#include <server_includes.h>
#include CSTDINT_H
#include <function/update_hash.h>
#include <session.h>

/**
  Set value to user variable.

  @param entry          pointer to structure representing variable
  @param set_null       should we set NULL value ?
  @param ptr            pointer to buffer with new value
  @param length         length of new value
  @param type           type of new value
  @param cs             charset info for new value
  @param dv             derivation for new value
  @param unsigned_arg   indiates if a value of type INT_RESULT is unsigned

  @note Sets error and fatal error if allocation fails.

  @retval
    false   success
  @retval
    true    failure
*/

#define extra_size      sizeof(double)

bool
update_hash(user_var_entry *entry, bool set_null, void *ptr, uint32_t length,
            Item_result i_type, const CHARSET_INFO * const cs, Derivation dv,
            bool unsigned_arg)
{
  if (set_null)
  {
    /// ALIGN_SIZE    global.h
    char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
    if (entry->value && entry->value != pos)
      free(entry->value);
    entry->value= 0;
    entry->length= 0;
  }
  else
  {
    if (i_type == STRING_RESULT)
      length++; // Store strings with end \0
    if (length <= extra_size)
    {
      /* Save value in value struct */
      char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
      if (entry->value != pos)
      {
         if (entry->value)
           free(entry->value);
           entry->value=pos;
      }
    }
    else
    {
      /* Allocate variable */
      if (entry->length != length)
      {
        char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
        if (entry->value == pos)
                                entry->value=0;
        void *tmpptr= ::realloc(entry->value, length);
        if (tmpptr == NULL)
          return 1;
        entry->value= (char *)tmpptr;
      }
    }
    if (i_type == STRING_RESULT)
    {
      length--;             // Fix length change above
      entry->value[length]= 0; // Store end \0
    }
    ::memcpy(entry->value,ptr,length);
    if (i_type == DECIMAL_RESULT)
                  ((my_decimal*)entry->value)->fix_buffer_pointer();
    entry->length= length;
    entry->collation.set(cs, dv);
    entry->unsigned_flag= unsigned_arg;
  }
  entry->type=i_type;
  return 0;
}
